#include "drv_led.h"

#define DRV_DEBUG
#define LOG_TAG "led"
#include <drv_log.h>

#define SYS_LED_PIN     BSP_SYS_LED_PIN 
#define CAN_LED_PIN     BSP_CAN_LED_PIN

#define SYS_LED_ACTIVE_LEVEL    BSP_SYS_LED_ACTIVE_LEVEL
#define CAN_LED_ACTIVE_LEVEL    BSP_CAN_LED_ACTIVE_LEVEL

struct rt_led_device
{
    struct rt_device parent;
    rt_uint8_t status;
    rt_uint8_t pin;
    rt_uint8_t active_level;
};

static struct rt_led_device led_dev[2] = {
    {
        .pin = SYS_LED_PIN, 
        .active_level = SYS_LED_ACTIVE_LEVEL,
    },
    {
        .pin = CAN_LED_PIN, 
        .active_level = CAN_LED_ACTIVE_LEVEL,
    },
}; 

static rt_ssize_t led_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    uint8_t status = *(uint8_t *)buffer;
    struct rt_led_device *_dev = (struct rt_led_device *)dev;
    if(_dev->status != status)
    {
        _dev->status = status;
        rt_pin_write(_dev->pin, _dev->status);
    }
    return 1;
}
static rt_ssize_t led_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    struct rt_led_device *_dev = (struct rt_led_device *)dev;
    *(uint8_t *)buffer = _dev->status; 
    return 1;
}

static rt_err_t led_control(rt_device_t dev, int cmd, void *args)
{
    struct rt_led_device *_dev;
    rt_uint8_t status;

    RT_ASSERT(dev != RT_NULL);

    _dev = (struct rt_led_device *)dev;

    switch(cmd)
    {
        case CMD_LED_ON:
            status = _dev->active_level;
            led_write(dev, 0, &status, 1);
            break;
        case CMD_LED_OFF:
            status = !_dev->active_level;
            led_write(dev, 0, &status, 1);
            break;
        case CMD_LED_TOGGLE:
            status = !_dev->status;
            led_write(dev, 0, &status, 1);
            break;
    }
}
static int rt_hw_led_init(void)
{
    for(rt_uint8_t i = 0; i < 2; i++)
    {
        rt_pin_mode(led_dev[i].pin, PIN_MODE_OUTPUT);
        rt_pin_write(led_dev[i].pin, !led_dev[i].active_level);
        led_dev[i].status = !led_dev[i].active_level;
        led_dev[i].parent.type = RT_Device_Class_Char;
        led_dev[i].parent.init = RT_NULL;
        led_dev[i].parent.open = RT_NULL;
        led_dev[i].parent.close = RT_NULL;
        led_dev[i].parent.write = led_write;
        led_dev[i].parent.read = led_read;
        led_dev[i].parent.control = led_control;
    }
    rt_device_register(&led_dev[0].parent, "sys_led", RT_DEVICE_FLAG_RDWR);
    rt_device_register(&led_dev[1].parent, "can_led", RT_DEVICE_FLAG_RDWR);
    return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_led_init);

static void led_ctrl(int argc, char **argv)
{
    rt_device_t dev;
    if(argc != 3)
    {
        rt_kprintf("Usage: %s <sys_led|can_led> <on|off|toggle>\n", argv[0]);
        return;
    }
    dev = rt_device_find(argv[1]);
    if(dev == RT_NULL)
    {
        rt_kprintf("device %s not found\n", argv[1]);
        return;
    }
    rt_device_open(dev, RT_DEVICE_OFLAG_OPEN);
    if(!strcmp(argv[2], "on"))
    {
        rt_device_control(dev, CMD_LED_ON, RT_NULL);
    }
    else if(!strcmp(argv[2], "off"))
    {
        rt_device_control(dev, CMD_LED_OFF, RT_NULL);
    }
    else if(!strcmp(argv[2], "toggle"))
    {
        rt_device_control(dev, CMD_LED_TOGGLE, RT_NULL);
    }
    else
    {
        rt_kprintf("Usage: %s <sys_led|can_led> <on|off|toggle>\n", argv[0]);
    }
    rt_device_close(dev);
}
MSH_CMD_EXPORT(led_ctrl, led control);